#!/usr/bin/python
# By Dmitry Rubinstein, dimrub@gmail.com, 24 Mar 2008
# Distributed under the GPLv3 license

import urllib
import urllib2
import re
import hashlib

login_url = 'http://www.livejournal.com/login.bml'
flat_url = 'http://www.livejournal.com/interface/flat'

class lj:
    def __init__(this, username, password):
        this.username = username
        this.password = password

    def getChallenge(this):
        res = this.makeLJRequestRaw({
            'mode': 'getchallenge'
        })
        if not 'success' in res or res['success'] != 'OK' or not 'challenge' in res:
            raise Exception("Failed to get a challenge from LJ: %s" % res['errmsg'])

        challenge = res['challenge']
        auth_resp = hashlib.md5(challenge + hashlib.md5(this.password).hexdigest()).hexdigest()
        return challenge, auth_resp

    def makeLJRequestRaw(this, data):
        params = urllib.urlencode(data)
        req = urllib2.Request(url=flat_url, data=params)
        result_file = urllib2.urlopen(req)
        field_name = ''
        res = dict()
        for i, line in enumerate(result_file):
            line = line.rstrip()
            if i % 2 == 0:
                field_name = line
            else:
                res[field_name] = line
        return res

    def makeLJRequest(this, data):
        if not 'mode' in data:
            raise Exception('Request is missing the \'mode\' field')
        chal, resp = this.getChallenge()
        data['auth_method'] = 'challenge'
        data['auth_challenge'] = chal
        data['auth_response'] = resp
        data['user'] = this.username
        res = this.makeLJRequestRaw(data)
        if (not 'success' in res) or (res['success'] != 'OK'):
            raise Exception("Failed to perform an LJ method %s: %s" % (data['mode'], res['errmsg']))
        return res

# Returns a map with the group name as a key and group id as a value
    def get_groups_list(this):
        res = this.makeLJRequest({
            'mode': 'getfriendgroups', 
            })
        groups = []
        for k, v in res.iteritems():
            m = re.match('frgrp_(?P<id>\d+)_name', k)
            if m:
                groups.append({'name': v, 'id': int(m.group('id'))})
        return groups

    def get_group_members(this, id):
        res = this.makeLJRequest({
            'mode': 'getfriends',
            'includegroups': 1
        })
        friends = dict()
        if not 'friend_count' in res:
            raise Exception("Error: couldn't find the friends count in response")
        count = res['friend_count']
        for i in range(1, int(count)):
            if "friend_%d_groupmask" % i in res:
                mask = int(res["friend_%d_groupmask" % i])
            else:
                mask = 0
            # Make 0 a special case, since LJ does not provide a group mask
            # for friends that are not members of any group
            if (id == 0) or (mask & (1<<id)):
                if "friend_%d_type" % i in res:
                    type = res["friend_%d_type" % i]
                else:
                    type = 'regular'
                friends[res["friend_%d_user" % i]] = {
                    'name': res["friend_%d_name" % i], 
                    'type': type
                }
        return friends

